#lang scribble/doc
@(require scribble/manual "guide-utils.rkt"
          (for-label racket/flonum
                     racket/unsafe/ops
                     racket/performance-hint
                     ffi/unsafe))

@;{@title[#:tag "performance"]{Performance}}
@title[#:tag "performance"]{性能}

@section-index["benchmarking"]
@section-index["speed"]

@;{Alan Perlis famously quipped ``Lisp programmers know the value of
everything and the cost of nothing.'' A Racket programmer knows, for
example, that a @racket[lambda] anywhere in a program produces a value
that is closed over its lexical environment---but how much does
allocating that value cost? While most programmers have a reasonable
grasp of the cost of various operations and data structures at the
machine level, the gap between the Racket language model and the
underlying computing machinery can be quite large.}
艾伦·珀利斯（Alan Perlis）有一句著名的话：“Lisp程序员知道一切的价值，而不知道一切的代价（Lisp programmers know the value of
everything and the cost of nothing）。”比如，一个Racket程序员知道，程序中任何地方的@racket[lambda]都会生成一个在其词法环境中封闭的值——但是分配这个值要多少代价呢？尽管大多数程序员对机器级别的各种操作和数据结构的成本有合理的把握，但Racket语言模型和计算机底层之间的差距可能相当大。

@;{In this chapter, we narrow the gap by explaining details of the
Racket compiler and run-time system and how they affect the run-time
and memory performance of Racket code.}
本章，我们通过解释Racket编译器和运行时系统的细节以及它们如何影响Racket代码的运行时间和内存性能来缩小这一差距。

@; ----------------------------------------------------------------------

@;{@section[#:tag "DrRacket-perf"]{Performance in DrRacket}}
@section[#:tag "DrRacket-perf"]{DrRacket中的性能}

@;{By default, DrRacket instruments programs for debugging, and
debugging instrumentation (provided by the
@other-doc['(lib "errortrace/scribblings/errortrace.scrbl")]
library) can significantly degrade performance for
some programs. Even when debugging is disabled through the
@onscreen{Choose Language...} dialog's @onscreen{Show Details} panel,
the @onscreen{Preserve stacktrace} checkbox is clicked by default,
which also affects performance. Disabling debugging and stacktrace
preservation provides performance results that are more consistent
with running in plain @exec{racket}.}
默认情况下，DrRacket对程序进行调试，而调试工具（由@other-doc['(lib "errortrace/scribblings/errortrace.scrbl")]库所提供）会显著地降低某些程序的性能。即使通过@onscreen{Choose Language...}对话框的@onscreen{Show Details（显示详细信息）}面板被禁用调试，默认情况下也会单击@onscreen{Preserve stacktrace}复选框，这也会影响性能。禁用调试和堆栈跟踪保留提供了与在纯@exec{racket}中运行更一致的效果。

@;{Even so, DrRacket and programs developed within DrRacket use the same
Racket virtual machine, so garbage collection times (see
@secref["gc-perf"]) may be longer in DrRacket than when a program is
run by itself, and DrRacket threads may impede execution of program
threads. @bold{For the most reliable timing results for a program, run in
plain @exec{racket} instead of in the DrRacket development environment.}
Non-interactive mode should be used instead of the
@tech["REPL"] to benefit from the module system. See
@secref["modules-performance"] for details.}
即便如此，DrRacket和在DrRacket中开发的程序使用同一个Racket虚拟机，
因此在DrRacket中垃圾收集时间（请参见《@secref["gc-perf"]》）可能比程序本身运行时间更长，并且DrRacket线程可能会妨碍程序线程的执行。@bold{要获得程序最可靠的的计时结果，请在普通的@exec{racket}中运行而不是在DrRacket开发环境中运行。}应使用非交互式模式而不是@tech["REPL"]，以便受益于模块系统。详情请参见《@secref["modules-performance"]》。

@; ----------------------------------------------------------------------

@;{@section[#:tag "JIT"]{The Bytecode and Just-in-Time (JIT) Compilers}}
@section[#:tag "JIT"]{字节码和实时（JIT）编译器}

@;{Every definition or expression to be evaluated by Racket is compiled
to an internal bytecode format. In interactive mode, this compilation
occurs automatically and on-the-fly. Tools like @exec{raco make} and
@exec{raco setup} marshal compiled bytecode to a file, so that you do
not have to compile from source every time that you run a
program. (Most of the time required to compile a file is actually in
macro expansion; generating bytecode from fully expanded code is
relatively fast.) See @secref["compile"] for more information on
generating bytecode files.}
每个要被Racket求值的定义或表达式都被编译成内部字节码格式。在交互模式下，此编译会自动进行且在运行中进行。像@exec{raco make}和@exec{raco setup}这样的工具将编译的字节码编组到一个文件中，这样你就不必每次运行程序时都从源代码进行编译。（编译文件所需的大部分时间实际上花费在宏展开中；从完全展开的代码生成字节码是比较快的。）有关生成字节码文件的详细信息，请参见《@secref["compile"]》。

@;{The bytecode compiler applies all standard optimizations, such as
constant propagation, constant folding, inlining, and dead-code
elimination. For example, in an environment where @racket[+] has its
usual binding, the expression @racket[(let ([x 1] [y (lambda () 4)]) (+
1 (y)))] is compiled the same as the constant @racket[5].}
字节码编译器应用所有标准优化，比如常量传输、常量折叠、内联和死代码消除。例如，在@racket[+]具有其通常绑定的环境中，表达式@racket[(let ([x 1] [y (lambda () 4)]) (+ 1 (y)))]的编译与常量@racket[5]相同。

@;{On some platforms, bytecode is further compiled to native code via a
@deftech{just-in-time} or @deftech{JIT} compiler. The @tech{JIT}
compiler substantially speeds programs that execute tight loops,
arithmetic on small integers, and arithmetic on inexact real
numbers. Currently, @tech{JIT} compilation is supported for x86,
x86_64 (a.k.a. AMD64), ARM, and 32-bit PowerPC processors. The @tech{JIT}
compiler can be disabled via the @racket[eval-jit-enabled] parameter
or the @DFlag{no-jit}/@Flag{j} command-line flag for @exec{racket}.}
在某些平台上，字节码通过@deftech{just-in-time}或@deftech{JIT}编译器进一步编译成本机代码。@tech{JIT}编译器大大加快了执行紧凑循环、小整数算法以及不精确实数算法的程序。目前，x86、x86_64（也称为AMD64）、ARM和32位PowerPC处理器支持@tech{JIT}编译。@tech{JIT}编译器可以通过@exec{racket}的@racket[eval-jit-enabled]参数或@exec{racket}的@DFlag{no-jit}/@Flag{j}命令行标志禁用。

@;{The @tech{JIT} compiler works incrementally as functions are applied,
but the @tech{JIT} compiler makes only limited use of run-time
information when compiling procedures, since the code for a given
module body or @racket[lambda] abstraction is compiled only once. The
@tech{JIT}'s granularity of compilation is a single procedure body,
not counting the bodies of any lexically nested procedures. The
overhead for @tech{JIT} compilation is normally so small that it is
difficult to detect.}
@tech{JIT}编译器在应用函数时以增量方式工作，但@tech{JIT}编译器在编译过程时仅有限地使用运行时信息，因为给定的模块主体或@racket[lambda]抽象只编译一次。@tech{JIT}的编译粒度是单个过程主体，不包含任何词汇嵌套过程的主体。@tech{JIT}编译的开销通常很小，难以检测。

@; ----------------------------------------------------------------------

@;{@section[#:tag "modules-performance"]{Modules and Performance}}
@section[#:tag "modules-performance"]{模块和性能}

@;{The module system aids optimization by helping to ensure that
identifiers have the usual bindings. That is, the @racket[+] provided
by @racketmodname[racket/base] can be recognized by the compiler and
inlined, which is especially important for @tech{JIT}-compiled code.
In contrast, in a traditional interactive Scheme system, the top-level
@racket[+] binding might be redefined, so the compiler cannot assume a
fixed @racket[+] binding (unless special flags or declarations
are used to compensate for the lack of a module system).}
模块系统通过帮助确保标识具有通常的绑定来帮助优化。也就是说，编译器可以识别@racketmodname[racket/base]提供的@racket[+]并进行内联，这对@tech{JIT}编译的代码尤为重要。相反，在传统的交互式Scheme系统中，顶级的@racket[+]绑定可能会被重新定义，因此编译器不能假定固定的@racket[+]绑定（除非使用特殊标志或声明来弥补模块系统的不足）。

@;{Even in the top-level environment, importing with @racket[require]
enables some inlining optimizations. Although a @racket[+] definition
at the top level might shadow an imported @racket[+], the shadowing
definition applies only to expressions evaluated later.}
即使在顶级环境中，使用@racket[require]导入也可以实现一些内联优化。尽管顶层的@racket[+]定义可能会对导入的@racket[+]进行覆盖，但覆盖定义仅适用于稍后求值的表达式。

@;{Within a module, inlining and constant-propagation optimizations take
additional advantage of the fact that definitions within a module
cannot be mutated when no @racket[set!] is visible at compile
time. Such optimizations are unavailable in the top-level
environment. Although this optimization within modules is important
for performance, it hinders some forms of interactive development and
exploration. The @racket[compile-enforce-module-constants] parameter
disables the @tech{JIT} compiler's assumptions about module
definitions when interactive exploration is more important. See
@secref["module-set"] for more information.}
在一个模块中，内联和常量传播优化还利用了这样一个事实，即当没有@racket[set!]时，模块中的定义不能发生变化在编译时可见。此类优化在顶级环境中不可用。尽管模块内的这种优化对性能很重要，但它阻碍了某些形式的交互开发和探索。当交互式探索更重要时，@racket[compile-enforce-module-constants]参数禁用@tech{JIT}编译器关于模块定义的假设。有关更多信息，请参阅《@secref["module-set"]》。

@;{The compiler may inline functions or propagate constants across module
boundaries. To avoid generating too much code in the case of function
inlining, the compiler is conservative when choosing candidates for
cross-module inlining; see @secref["func-call-performance"] for
information on providing inlining hints to the compiler.}
编译器可以内联函数或跨模块边界传播常量。为了避免在函数内联的情况下生成过多的代码，编译器在选择跨模块内联的候选时是保守的；有关向编译器提供内联提示的信息，请参阅《@secref["func-call-performance"]》。

@;{The later section @secref["letrec-performance"] provides some
additional caveats concerning inlining of module bindings.}
后边《@secref["letrec-performance"]》部分提供一些关于模块绑定内联的附加说明。

@; ----------------------------------------------------------------------

@;{@section[#:tag "func-call-performance"]{Function-Call Optimizations}}
@section[#:tag "func-call-performance"]{函数调用优化}

@;{When the compiler detects a function call to an immediately visible
function, it generates more efficient code than for a generic call,
especially for tail calls. For example, given the program}
当编译器检测到对即时可见函数的函数调用时，它会生成比泛型调用更高效的代码，尤其是尾部调用。例如，给定程序

@racketblock[
(letrec ([odd (lambda (x) 
                (if (zero? x) 
                    #f 
                    (even (sub1 x))))] 
         [even (lambda (x) 
                 (if (zero? x) 
                     #t 
                     (odd (sub1 x))))]) 
  (odd 40000000))
]

@;{the compiler can detect the @racket[odd]--@racket[even] loop and
produce code that runs much faster via loop unrolling and related
optimizations.}
编译器可以检测到@racket[odd]——@racket[even]循环并通过循环展开和相关优化生成运行速度更快的代码。

@;{Within a module form, @racket[define]d variables are lexically scoped
like @racket[letrec] bindings, and definitions within a module
therefore permit call optimizations, so}
在模块表里，@racket[define]变量在词汇上的作用域类似于@racket[letrec]绑定，因此模块中的定义允许调用优化，因此

@racketblock[
(define (odd x) ....)
(define (even x) ....)
]

@;{within a module would perform the same as the @racket[letrec] version.}
在一个模块中，它将执行与@racket[letrec]版本相同的操作。

@;{For direct calls to functions with keyword arguments, the compiler can
typically check keyword arguments statically and generate a direct
call to a non-keyword variant of the function, which reduces the
run-time overhead of keyword checking. This optimization applies only
for keyword-accepting procedures that are bound with @racket[define].}
对于直接调用带有关键字参数的函数，编译器通常可以静态检查关键字参数，并生成对函数的非关键字变量的直接调用，这减少了关键字检查的运行时开销。此优化仅适用于与@racket[define]绑定的关键字接受过程。

@;{For immediate calls to functions that are small enough, the compiler
may inline the function call by replacing the call with the body of
the function. In addition to the size of the target function's body,
the compiler's heuristics take into account the amount of inlining
already performed at the call site and whether the called function
itself calls functions other than simple primitive operations. When a
module is compiled, some functions defined at the module level are
determined to be candidates for inlining into other modules; normally,
only trivial functions are considered candidates for cross-module
inlining, but a programmer can wrap a function definition with
@racket[begin-encourage-inline] to encourage inlining
of the function.}
对于对足够小的函数的即时调用，编译器可以通过用函数主体替换调用来内联函数调用。除了目标函数体的大小之外，编译器的试探法还考虑了在调用位置已经执行的内联数量，以及被调用函数本身是否调用了简单基元操作以外的函数。当编译模块时，在模块级定义的一些函数被确定为内联到其它模块的候选函数；通常情况下，只有足够小的函数才被认为是跨模块内联的候选函数，但程序员可以用@racket[begin-encourage-inline]包装函数定义，以鼓励函数内联。

@;{Primitive operations like @racket[pair?], @racket[car], and
@racket[cdr] are inlined at the machine-code level by the @tech{JIT}
compiler. See also the later section @secref["fixnums+flonums"] for
information about inlined arithmetic operations.}
像@racket[pair?]、@racket[car]和@racket[cdr]这样的基础操作是在机器代码级被@tech{JIT}编译器内联的。有关内联运算活动的信息，请参见后面的《@secref["fixnums+flonums"]》。

@; ----------------------------------------------------------------------

@;{@section{Mutation and Performance}}
@section[#:tag "Mutation_and_Performance"]{突变和性能}

@;{Using @racket[set!] to mutate a variable can lead to bad
performance. For example, the microbenchmark}
利用@racket[set!]突变变量可能导致性能下降。例如，小规模基准测试

@racketmod[
racket/base

(define (subtract-one x)
  (set! x (sub1 x))
  x)

(time
  (let loop ([n 4000000])
    (if (zero? n)
        'done
        (loop (subtract-one n)))))
]

@;{runs much more slowly than the equivalent}
运行速度比同等速度慢得多

@racketmod[
racket/base

(define (subtract-one x)
  (sub1 x))

(time
  (let loop ([n 4000000])
    (if (zero? n)
        'done
        (loop (subtract-one n)))))
]

@;{In the first variant, a new location is allocated for @racket[x] on
every iteration, leading to poor performance. A more clever compiler
could unravel the use of @racket[set!] in the first example, but since
mutation is discouraged (see @secref["using-set!"]), the compiler's
effort is spent elsewhere.}
在第一个变量中，每次迭代都会为@racket[x]分配一个新位置，导致性能不佳。在第一个例子中，一个更聪明的编译器可以破解@racket[set!]的用法，由于不鼓励突变（参见《@secref["using-set!"]》），编译器的努力就花在了其它地方。

@;{More significantly, mutation can obscure bindings where inlining and
constant-propagation might otherwise apply. For example, in}
更重要的是，突变可能会模糊绑定，否则可能会应用内联和常量传播。例如，在

@racketblock[
(let ([minus1 #f])
  (set! minus1 sub1)
  (let loop ([n 4000000])
    (if (zero? n)
        'done
        (loop (minus1 n)))))
]

@;{the @racket[set!] obscures the fact that @racket[minus1] is just
another name for the built-in @racket[sub1].}
@racket[set!]掩盖了@racket[minus1]只是内置的@racket[sub1]的另一个名字的事实。

@; ----------------------------------------------------------------------

@;{@section[#:tag "letrec-performance"]{@racket[letrec] Performance}}
@section[#:tag "letrec-performance"]{@racket[letrec]性能}

@;{When @racket[letrec] is used to bind only procedures and literals,
then the compiler can treat the bindings in an optimal manner,
compiling uses of the bindings efficiently. When other kinds of
bindings are mixed with procedures, the compiler may be less able to
determine the control flow.}
当@racket[letrec]仅用于绑定过程和文本时，编译器可以以最佳方式处理绑定，有效地编译绑定的使用。当其它类型的绑定与过程混合时，编译器可能无法能确定控制流。

@;{For example,}
例如，

@racketblock[
(letrec ([loop (lambda (x) 
                (if (zero? x) 
                    'done
                    (loop (next x))))] 
         [junk (display loop)]
         [next (lambda (x) (sub1 x))])
  (loop 40000000))
]

@;{likely compiles to less efficient code than}
可能编译成比以下内容效率更低的代码

@racketblock[
(letrec ([loop (lambda (x) 
                (if (zero? x) 
                    'done
                    (loop (next x))))] 
         [next (lambda (x) (sub1 x))])
  (loop 40000000))
]

@;{In the first case, the compiler likely does not know that
@racket[display] does not call @racket[loop]. If it did, then
@racket[loop] might refer to @racket[next] before the binding is
available.}
在第一种情况下，编译器可能不知道@racket[display]没有调用@racket[loop]。如果是，那么@racket[loop]可能会在绑定之前引用@racket[next]是可获得的。

@;{This caveat about @racket[letrec] also applies to definitions of
functions and constants as internal definitions or in modules. A
definition sequence in a module body is analogous to a sequence of
@racket[letrec] bindings, and non-constant expressions in a module
body can interfere with the optimization of references to later
bindings.}
关于@racket[letrec]的这个警告也适用于作为内部定义或模块中的函数和常量的定义。模块主体中的定义序列类似于@racket[letrec]绑定的序列，模块主体中非常量表达式可能会干扰对后面绑定的引用的优化。

@; ----------------------------------------------------------------------

@;{@section[#:tag "fixnums+flonums"]{Fixnum and Flonum Optimizations}}
@section[#:tag "fixnums+flonums"]{Fixnum和Flonum优化}

@;{A @deftech{fixnum} is a small exact integer. In this case, ``small''
depends on the platform. For a 32-bit machine, numbers that can be
expressed in 30 bits plus a sign bit are represented as fixnums. On a
64-bit machine, 62 bits plus a sign bit are available.}
@deftech{fixnum}是一个小的精确整数。在这种情况下，“小”取决于平台。对于32位机器，可以用30位加上一个符号位表示的数字代表为fixnum。在64位机器上，62位加上一个符号位是有效的。

@;{A @deftech{flonum} is used to represent any inexact real number. They
correspond to 64-bit IEEE floating-point numbers on all platforms.}
@deftech{flonum}用来表示任何不精确的实数。它们对应于所有平台上的64位IEEE浮点数。

@;{Inlined fixnum and flonum arithmetic operations are among the most
important advantages of the @tech{JIT} compiler. For example, when
@racket[+] is applied to two arguments, the generated machine code
tests whether the two arguments are fixnums, and if so, it uses the
machine's instruction to add the numbers (and check for overflow). If
the two numbers are not fixnums, then it checks whether
both are flonums; in that case, the machine's floating-point
operations are used directly. For functions that take any number of
arguments, such as @racket[+], inlining works for two or more
arguments (except for @racket[-], whose one-argument case is also
inlined) when the arguments are either all fixnums or all flonums.}
内联fixnum和flonum算术运算是@tech{JIT}编译器最重要的优点之一。例如，当@racket[+]应用于两个参数时，生成的机器代码测试这两个参数是否为fixnum，如果是，则使用机器的指令将数字相加（并检查溢出）。如果这两个数字不是fixnum，则检查是否两者都是flonum；在这种情况下，计算机的浮点直接使用操作。对于采用任意数量的参数的函数，如@racket[+]，当参数全部为fixnum或全部为flonum时，内联适用于两个或多个参数（除了@racket[-]，其一个参数大小写也是内联的）。

@;{Flonums are typically @defterm{boxed}, which means that memory is
allocated to hold every result of a flonum computation. Fortunately,
the generational garbage collector (described later in
@secref["gc-perf"]) makes allocation for short-lived results
reasonably cheap. Fixnums, in contrast are never boxed, so they are
typically cheap to use.}
flonum通常是@defterm{盒装的（boxed）}，这意味着分配内存来保存flonum计算的每一个结果。幸运的是，分代垃圾回收器（稍后在《@secref["gc-perf"]》中描述）使短期结果的分配开销相当小。相比之下，fixnum从不装盒，因此通常使用起来开销都很小。

@;{@margin-note{See @secref["effective-futures"] for an example use of
@tech{flonum}-specific operations.}}
@margin-note{有关@tech{flonum}特定操作的示例用法，请参见《@secref["effective-futures"]》。}

@;{The @racketmodname[racket/flonum] library provides flonum-specific
operations, and combinations of flonum operations allow the @tech{JIT}
compiler to generate code that avoids boxing and unboxing intermediate
results. Besides results within immediate combinations,
flonum-specific results that are bound with @racket[let] and consumed
by a later flonum-specific operation are unboxed within temporary
storage. Finally, the compiler can detect some flonum-valued loop
accumulators and avoid boxing of the accumulator. The bytecode
decompiler (see @secref[#:doc '(lib "scribblings/raco/raco.scrbl")
"decompile"]) annotates combinations where the JIT can avoid boxes with
@racketidfont{#%flonum}, @racketidfont{#%as-flonum}, and
@racketidfont{#%from-flonum}.}
@racketmodname[racket/flonum]库提供了flonum特定的操作，flonum操作的组合允许@tech{JIT}编译器生成避免装盒和拆盒中间结果的代码。除了即时组合中的结果外，与@racket[let]绑定并由后续特定于flonum的操作使用的特定于flonum的结果在临时存储中未装盒。最后，编译器可以检测一些flonum值循环累加器并避免累加器装盒。字节码反编译程序（见《@secref[#:doc '(lib "scribblings/raco/raco.scrbl") "decompile"]》）注释JIT可以避免使用@racketidfont{#%flonum}、@racketidfont{#%as-flonum}和@racketidfont{#%from-flonum}盒子。

@;{@margin-note{Unboxing of local bindings and accumulators is not
supported by the JIT for PowerPC.}}
@margin-note{PowerPC的JIT不支持局部绑定和累加器的装盒。}

@;{The @racketmodname[racket/unsafe/ops] library provides unchecked
fixnum- and flonum-specific operations. Unchecked flonum-specific
operations allow unboxing, and sometimes they allow the compiler to
reorder expressions to improve performance. See also
@secref["unchecked-unsafe"], especially the warnings about unsafety.}
@racketmodname[racket/unsafe/ops]库提供了未经检查的fixnum和flonum特定操作。未选中的flonum特定操作允许取消装盒，有时它们允许编译器重新排序表达式以提高性能。另请参见《@secref["unchecked-unsafe"]》，尤其是关于不安全的警告。

@; ----------------------------------------------------------------------

@;{@section[#:tag "unchecked-unsafe"]{Unchecked, Unsafe Operations}}
@section[#:tag "unchecked-unsafe"]{未检查、不安全的操作}

@;{The @racketmodname[racket/unsafe/ops] library provides functions that
are like other functions in @racketmodname[racket/base], but they
assume (instead of checking) that provided arguments are of the right
type. For example, @racket[unsafe-vector-ref] accesses an element from
a vector without checking that its first argument is actually a vector
and without checking that the given index is in bounds. For tight
loops that use these functions, avoiding checks can sometimes speed
the computation, though the benefits vary for different unchecked
functions and different contexts.}
@racketmodname[racket/unsafe/ops]库提供的函数与@racketmodname[racket/base]中的其它函数类似，但它们假定（而不是检查）提供的参数类型正确。例如，@racket[unsafe-vector-ref]从一个向量中访问一个元素，而不检查它的第一个参数实际上是一个向量，也不检查给定的索引是否在边界内。对于使用这些函数的紧凑循环，避免检查有时可以加快计算速度，尽管不同的未检查函数和不同上下文有不同的好处。

@;{Beware that, as ``unsafe'' in the library and function names suggest,
misusing the exports of @racketmodname[racket/unsafe/ops] can lead to
crashes or memory corruption.}
请注意，正如库和函数名称中的“不安全（unsafe）”所暗示的那样，错误使用@racketmodname[racket/unsafe/ops]的导出可能会导致崩溃或内存损坏。

@; ----------------------------------------------------------------------

@;{@section[#:tag "ffi-pointer-access"]{Foreign Pointers}}
@section[#:tag "ffi-pointer-access"]{外部指针}

@;{The @racketmodname[ffi/unsafe] library provides functions for unsafely
reading and writing arbitrary pointer values. The JIT recognizes uses
of @racket[ptr-ref] and @racket[ptr-set!] where the second argument is
a direct reference to one of the following built-in C types:
@racket[_int8], @racket[_int16], @racket[_int32], @racket[_int64],
@racket[_double], @racket[_float], and @racket[_pointer]. Then, if the
first argument to @racket[ptr-ref] or @racket[ptr-set!] is a C pointer
(not a byte string), then the pointer read or write is performed
inline in the generated code.}
@racketmodname[ffi/unsafe]库提供了不安全读取和写入任意指针值的函数。JIT识别@racket[ptr-ref]和@racket[ptr-set!]的用法，其中第二个参数是对以下内置C类型之一的直接引用：@racket[_int8]、@racket[_int16]、@racket[_int32]、@racket[_int64]、
@racket[_double]、@racket[_float]和@racket[_pointer]。然后，如果第一个参数是@racket[ptr-ref]和@racket[ptr-set!]是C指针（不是字节字符串），则指针的读取或写入在生成的代码中内联执行。

@;{The bytecode compiler will optimize references to integer
abbreviations like @racket[_int] to C types like
@racket[_int32]---where the representation sizes are constant across
platforms---so the JIT can specialize access with those C types. C
types such as @racket[_long] or @racket[_intptr] are not constant
across platforms, so their uses are currently not specialized by the
JIT.}
字节码编译器会优化对整数缩写的引用，如@racket[_int]到C类型（如@racket[_int32]），其中表示大小在平台之间是恒定的，因此JIT可以专门访问这些C类型。C类型（如@racket[_long]或@racket[_intptr]）在不同平台上是不恒定的，因此它们的使用目前没有被JIT专门化。

@;{Pointer reads and writes using @racket[_float] or @racket[_double] are
not currently subject to unboxing optimizations.}
使用@racket[_float]或@racket[_double]进行指针读取和写入目前不受拆盒优化的影响。

@; ----------------------------------------------------------------------

@;{@section[#:tag "regexp-perf"]{Regular Expression Performance}}
@section[#:tag "regexp-perf"]{正则表达式性能}

@;{When a string or byte string is provided to a function like
@racket[regexp-match], then the string is internally compiled into
a @tech{regexp} value. Instead of supplying a string or byte string
multiple times as a pattern for matching, compile the pattern once to
a @tech{regexp} value using @racket[regexp], @racket[byte-regexp],
@racket[pregexp], or @racket[byte-pregexp]. In place of a constant
string or byte string, write a constant @tech{regexp} using an
@litchar{#rx} or @litchar{#px} prefix.}
当一个字符串或字节字符串被提供给@racket[regexp-match]这样的函数时，该字符串将在内部编译为@tech{regexp}值。与其多次提供字符串或字节字符串作为匹配模式，不如使用@racket[regexp]、@racket[byte-regexp]、@racket[pregexp]或 @racket[byte-pregexp]将模式编译为@tech{regexp}值。代替常量字符串或字节字符串，使用@litchar{#rx}或@litchar{#px}前缀编写常量@tech{regexp}。

@racketblock[
(define (slow-matcher str)
  (regexp-match? "[0-9]+" str))

(define (fast-matcher str)
  (regexp-match? #rx"[0-9]+" str))

(define (make-slow-matcher pattern-str)
  (lambda (str)
    (regexp-match? pattern-str str)))

(define (make-fast-matcher pattern-str)
  (define pattern-rx (regexp pattern-str))
  (lambda (str)
    (regexp-match? pattern-rx str)))
]


@; ----------------------------------------------------------------------

@;{@section[#:tag "gc-perf"]{Memory Management}}
@section[#:tag "gc-perf"]{内存管理}

@;{The Racket implementation is available in two variants: @deftech{3m} and
@deftech{CGC}. The @tech{3m} variant uses a modern,
@deftech{generational garbage collector} that makes allocation
relatively cheap for short-lived objects. The @tech{CGC} variant uses
a @deftech{conservative garbage collector} which facilitates
interaction with C code at the expense of both precision and speed for
Racket memory management. The 3m variant is the standard one.}
Racket实现有两种变体：@tech{3m}和@deftech{CGC}。@tech{3m}变体使用了一个现代的@deftech{分代垃圾收集器}，使得对短期对象的分配开销相对较小。@tech{CGC}变体使用了一个@deftech{保守垃圾收集器}，它以牺牲Racket内存管理的精度和速度为代价，促进与C代码的交互。3m变体是标准变型。

@;{Although memory allocation is reasonably cheap, avoiding allocation
altogether is normally faster. One particular place where allocation
can be avoided sometimes is in @deftech{closures}, which are the
run-time representation of functions that contain free variables.
For example,}
虽然内存分配开销相当小，但完全避免分配通常更快。有时可以避免分配的一个特定位置是@deftech{closures（闭包）}，它包含自由变量函数的运行时表示。例如,

@racketblock[
(let loop ([n 40000000] [prev-thunk (lambda () #f)])
  (if (zero? n)
      (prev-thunk)
      (loop (sub1 n)
            (lambda () n))))
]

@;{allocates a closure on every iteration, since @racket[(lambda () n)]
effectively saves @racket[n].}
在每次迭代时分配一个闭包，因为@racket[(lambda () n)]有效地保护了@racket[n]。

@;{The compiler can eliminate many closures automatically. For example,
in}
编译器可以自动清除许多闭包。例如，在

@racketblock[
(let loop ([n 40000000] [prev-val #f])
  (let ([prev-thunk (lambda () n)])
    (if (zero? n)
        prev-val
        (loop (sub1 n) (prev-thunk)))))
]

@;{no closure is ever allocated for @racket[prev-thunk], because its only
application is visible, and so it is inlined. Similarly, in}
从来没有为@racket[prev-thunk]分配闭包，因为它的唯一应用程序是可见的，因此它是内联的。同样，在

@racketblock[
(let n-loop ([n 400000])
  (if (zero? n)
      'done
      (let m-loop ([m 100])
        (if (zero? m)
            (n-loop (sub1 n))
            (m-loop (sub1 m))))))
]

@;{then the expansion of the @racket[let] form to implement
@racket[m-loop] involves a closure over @racket[n], but the compiler
automatically converts the closure to pass itself @racket[n] as an
argument instead.}
然后，扩展@racket[let]表以实现@racket[m-loop]涉及到@racket[n]上的闭包，但编译器会自动转换闭包，将其作为参数传递给@racket[n]，但编译器会自动转换闭包，将其作为参数传递给@racket[n]。

@;{@section[#:tag "Reachability and Garbage Collection"]{Reachability and Garbage Collection}}
@section[#:tag "Reachability and Garbage Collection"]{可访问性和垃圾回收}

@;{In general, Racket re-uses the storage for a value when the
garbage collector can prove that the object is unreachable from
any other (reachable) value. Reachability is a low-level, 
abstraction breaking concept (and thus one must understand many
details of the runtime system's implementation to accurate predicate
precisely when values are reachable from each other),
but generally speaking one value is reachable from a second one when 
there is some operation to recover the original value from the second
one.}
通常，当垃圾回收器可以证明对象无法从任何其它（可访问）值访问时，Racket会重新使用存储空间来获取某个值。可访问性（reachability）是一个低级的，打破抽象的概念（因此，必须理解运行时系统实现精确判断的许多细节，准确地说，当值可以相互访问时），但一般来说，当存在从第二个值恢复原始值时，一个值可以从另一个值访问。 

@;{To help programmers understand when an object is no longer reachable and its
storage can be reused,
Racket provides @racket[make-weak-box] and @racket[weak-box-value],
the creator and accessor for a one-record struct that the garbage
collector treats specially. An object inside a weak box does not count
as reachable, and so @racket[weak-box-value] might return the object
inside the box, but it might also return @racket[#f] to indicate
that the object was otherwise unreachable and garbage collected.
Note that unless a garbage collection actually occurs, the value will
remain inside the weak box, even if it is unreachable.}
为了帮助程序员理解对象何时不再可访问并且其存储可以重用，Racket提供了@racket[make-weak-box]和@racket[weak-box-value]，垃圾回收器专门处理的一个记录结构的创建者和访问者。在弱盒子内的对象不被视为可访问，因此@racket[weak-box-value]可能会返回盒内的该对象，但也可能返回@racket[#f]以表示该对象以其它方式访问并被垃圾回收。请注意，除非垃圾回收实际发生，否则该值将保留在弱盒中，即使无法访问。

@;{For example, consider this program:}
例如，考虑这个程序：

@racketmod[racket
           (struct fish (weight color) #:transparent)
           (define f (fish 7 'blue))
           (define b (make-weak-box f))
           (printf "b has ~s\n" (weak-box-value b))
           (collect-garbage)
           (printf "b has ~s\n" (weak-box-value b))]

@;{It will print @litchar{b has #(struct:fish 7 blue)} twice because the
definition of @racket[f] still holds onto the fish. If the program
were this, however:}
它将打印两次@litchar{b has #(struct:fish 7 blue)}，因为@racket[f]的定义仍然适用于fish。然而，如果程序是这样的：

@racketmod[racket
           (struct fish (weight color) #:transparent)
           (define f (fish 7 'blue))
           (define b (make-weak-box f))
           (printf "b has ~s\n" (weak-box-value b))
           (set! f #f)
           (collect-garbage)
           (printf "b has ~s\n" (weak-box-value b))]

@;{the second printout will be @litchar{b has #f} because
no reference to the fish exists (other than the one in the box).}
第二次打印输出将是@litchar{b has #f}，因为不再存在对fish的引用（除了盒子里的那个）。

@;{As a first approximation, all values in Racket must be allocated and will
demonstrate behavior similar to the fish above.
There are a number of exceptions, however:}
作为第一个近似值，必须分配Racket中的所有值，并显示出与上述fish相似的行为。但也有一些例外情况：

@;{@itemlist[
 @item{Small integers (recognizable with @racket[fixnum?]) are
                always available without explicit
                allocation. From the perspective of the garbage collector
                and weak boxes, their storage is never reclaimed. (Due to
                clever representation techniques, however, their storage
                does not count towards the space that Racket uses.
                That is, they are effectively free.)}
    
         @item{Procedures where
               the compiler can see all of their call sites may never be
               allocated at all (as discussed above). 
               Similar optimizations may also eliminate 
               the allocation for other kinds of values.}
         @item{Interned symbols are allocated only once (per place). A table inside
               Racket tracks this allocation so a symbol may not become garbage
               because that table holds onto it.}
         @item{Reachability is only approximate with the @tech{CGC} collector (i.e.,
               a value may appear reachable to that collector when there is,
               in fact, no way to reach it anymore.}]}
{@itemlist[
@item{小整数（可使用@racket[fixnum?]识别）在没有明确分配的情况下始终可用。从垃圾回收器和弱盒子的角度来看，它们的存储永远不会被回收。（然而，由于巧妙的表达技巧，它们的存储空间不计入Racket使用的空间。也就是说，它们实际上是免费的。）}
@item{编译器可以看到其所有调用点的过程可能永远不会被分配（如上所述）。类似的优化也可以消除对其它类型值的分配。}
@item{交错符号仅分配一次（每个位置）。Racket中的表跟踪此分配，因此符号可能不会因为该表而成为垃圾。}
@item{可访问性仅与@tech{CGC}回收器（即，当实际上无法再访问某个值时，该回收器可能会看到该值是可访问的。}]

@;{@section{Weak Boxes and Testing}}
@section[#:tag "Weak_Boxes_and_Testing"]{弱盒及测试}

@;{One important use of weak boxes is in testing that some abstraction properly 
releases storage for data it no longer needs, but there is a gotcha that 
can easily cause such test cases to pass improperly. }
弱盒的一个重要用途是在测试某些抽象是否正确地释放了不再需要的数据的存储，但有一个问题很容易导致此类测试用例不正确地通过。

@;{Imagine you're designing a data structure that needs to
hold onto some value temporarily but then should clear a field or
somehow break a link to avoid referencing that value so it can be
collected. Weak boxes are a good way to test that your data structure
properly clears the value. This is, you might write a test case
that builds a value, extracts some other value from it
(that you hope becomes unreachable), puts the extracted value into a weak-box,
and then checks to see if the value disappears from the box.}
假设你正在设计一个数据结构，该数据结构需要暂时保存某个值，但应清除字段或以某种方式断开链接，以避免引用该值，以便回收该值。弱箱子是测试数据结构是否能正确清除值的好方法。也就是说，你可以编写一个测试用例，构建一个值，从中提取一些其他值（你希望它变得不可访问），将提取的值放入一个弱盒中，然后检查该值是否从盒子消失。

@;{This code is one attempt to follow that pattern, but it has a subtle bug:}
这段代码试图遵循这种模式，但它有一个微妙的错误：

@racketmod[racket
           (let* ([fishes (list (fish 8 'red)
                                (fish 7 'blue))]
                  [wb (make-weak-box (list-ref fishes 0))])
             (collect-garbage)
             (printf "still there? ~s\n" (weak-box-value wb)))]

@;{Specifically, it will show that the weak box is empty, but not
because @racket[_fishes] no longer holds onto the value, but
because @racket[_fishes] itself is not reachable anymore!}
具体来说，它会显示弱盒是空的，但这不是因为@racket[_fishes]不再保留这个值，而是因为@racket[_fishes]本身不再可访问！

@;{Change the program to this one:}
把程序改成这样：

@racketmod[racket
           (let* ([fishes (list (fish 8 'red)
                                (fish 7 'blue))]
                  [wb (make-weak-box (list-ref fishes 0))])
             (collect-garbage)
             (printf "still there? ~s\n" (weak-box-value wb))
             (printf "fishes is ~s\n" fishes))]

@;{and now we see the expected result. The difference is that last
occurrence of the variable @racket[_fishes]. That constitutes
a reference to the list, ensuring that the list is not itself
garbage collected, and thus the red fish is not either.}
现在我们看到了预期的结果。不同的是，变量@racket[_fishes]最后一次出现。这构成了对列表的引用，确保列表本身不是被垃圾回收的，因此red fish也不是。

@;{@section{Reducing Garbage Collection Pauses}}
@section[#:tag "Reducing_Garbage_Collection_Pauses"]{减少垃圾回收暂停}

@;{By default, Racket's @tech{generational garbage collector} creates
brief pauses for frequent @deftech{minor collections}, which inspect
only the most recently allocated objects, and long pauses for infrequent
@deftech{major collections}, which re-inspect all memory.}
默认情况下，Racket的@tech{分代垃圾回收器}为频繁的@deftech{小回收（minor collections）}创建短暂停，这只会检查最近分配的对象，为不频繁的@deftech{大回收（major collections）}创建长暂停，这会重新检查所有内存。

@;{For some applications, such as animations and games,
long pauses due to a major collection can interfere
unacceptably with a program's operation. To reduce major-collection
pauses, the Racket garbage collector supports @deftech{incremental
garbage-collection} mode. In incremental mode, minor collections
create longer (but still relatively short) pauses by performing extra
work toward the next major collection. If all goes well, most of a
major collection's work has been performed by minor collections the
time that a major collection is needed, so the major collection's
pause is as short as a minor collection's pause. Incremental mode
tends to run more slowly overall, but it can
provide much more consistent real-time behavior.}
对于某些应用程序，如动画和游戏，由于一个大集合而导致的长时间暂停可能会对程序的操作造成无法接受的干扰。为了减少大回收暂停，Racket垃圾回收器支持@deftech{增量式垃圾回收（incremental garbage-collection）}模式。在增量模式中，小回收通过向下一个大回收执行额外工作来创建更长（但仍然相对较短）的暂停。如果一切顺利，一个大回收的大部分工作都是由小回收完成的，在需要大回收的时候，所以大回收暂停和小回收暂停一样短。增量模式总体上运行更慢，但它可以提供更一致的实时行为。

@;{If the @envvar{PLT_INCREMENTAL_GC} environment variable is set
to a value that starts with @litchar{1}, @litchar{y}, or @litchar{Y}
when Racket starts, incremental mode is permanently enabled. Since
incremental mode is only useful for certain parts of some programs,
however, and since the need for incremental mode is a property of a
program rather than its environment, the preferred way to enable
incremental mode is with @racket[(collect-garbage 'incremental)].}
当在Racket启动时将@envvar{PLT_INCREMENTAL_GC}环境变量设置为以@litchar{1}、@litchar{y}或@litchar{Y}开头的值，则将永久启用增量模式。然而，由于增量模式仅对某些程序的某些部分有用，并且由于增量模式的需要是程序的属性而不是其环境的属性，因此启用增量模式的首选方式是使用@racket[(collect-garbage 'incremental)]。

@;{Calling @racket[(collect-garbage 'incremental)] does not perform an
immediate garbage collection, but instead requests that each minor
collection perform incremental work up to the next major collection.
The request expires with the next major collection. Make a call to
@racket[(collect-garbage 'incremental)] in any repeating task within
an application that needs to be responsive in real time. Force a
full collection with @racket[(collect-garbage)] just before an initial
@racket[(collect-garbage 'incremental)] to initiate incremental mode
from an optimal state.}
调用@racket[(collect-garbage 'incremental)]不会立即执行垃圾回收，而是请求每个小回收执行增量工作，直到下一个大回收。该请求将在下一次大回收时过期。在应用程序中需要实时响应的任何重复任务中调用@racket[(collect-garbage 'incremental)]。在初始@racket[(collect-garbage 'incremental)]之前，使用@racket[(collect-garbage)]强制进行完全回收，以从最佳状态启动增量模式。

@;{To check whether incremental mode is use and how it affects pause
times, enable @tt{debug}-level logging output for the
@racketidfont{GC} topic. For example,}
要检查是否使用了增量模式以及它如何影响暂停时间，请为@racketidfont{GC}主题启用@tt{debug}级日志记录输出。例如，

@commandline{racket -W "debuG@"@"GC error" main.rkt}

@;{runs @filepath{main.rkt} with garbage-collection logging to stderr
(while preserving @tt{error}-level logging for all topics). Minor
collections are reported by @litchar{min} lines, increment-mode minor
collection are reported with @litchar{mIn} lines, and major
collections are reported with @litchar{MAJ} lines.}
运行@filepath{main.rkt}并将垃圾回收日志记录到标准错误（stderr）（同时保留所有主题的@tt{error}级日志记录）。小回收由@litchar{min}行报告，增量模式小回收由@litchar{mIn}行报告，大回收由@litchar{MAJ（专业）}行报告。
